package com.andy.basics;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

public class FunctionCaller {

	private static final String CLASS = FunctionCaller.class.getName();

	private static final Logger DEBUG = Logger.getLogger(Constants.DEBUG_KEY + FunctionCaller.CLASS);

	private static FormatterFactory formatterFactory = new FormatterFactory();

	private static Map<String, Pattern> patternMap = new HashMap<String, Pattern>();

	private static Map<String, String[]> arrayMap = new HashMap<String, String[]>();

	public static Object call(final Object object, final String method) {
		if ((object == null) || (method == null)) {
			if (FunctionCaller.DEBUG.isDebugEnabled()) {
				FunctionCaller.DEBUG.debug("Some of the parameters are null");
			}
			return null;
		}
		if (FunctionCaller.DEBUG.isDebugEnabled()) {
			FunctionCaller.DEBUG.debug("Call() - Object " + object + " from class " + object.getClass().getName() + ", Method: "
					+ method);
		}
		try {
			return object.getClass().getDeclaredMethod(method);
		} catch (Exception e) {
			if (FunctionCaller.DEBUG.isDebugEnabled()) {
				FunctionCaller.DEBUG.debug("Exception: " + e.getMessage(), e);
			}
		}
		return null;
	}

	public static Object call(final Object object, final String method, final Object parameter) {
		if ((object == null) || (method == null) || (parameter == null)) {
			if (FunctionCaller.DEBUG.isDebugEnabled()) {
				FunctionCaller.DEBUG.debug("Some of the parameters are null");
			}
			return null;
		}
		if (FunctionCaller.DEBUG.isDebugEnabled()) {
			FunctionCaller.DEBUG.debug("Call() - Object " + object + " from class " + object.getClass().getName() + ", Method: "
					+ method + ", Parameter: " + parameter + " from class " + parameter.getClass().getName());
		}
		try {
			return tryCallUpward(object, method, parameter);
		} catch (Exception e) {
			if (FunctionCaller.DEBUG.isDebugEnabled()) {
				FunctionCaller.DEBUG.debug("Exception: " + e.getMessage(), e);
			}
		}
		return null;
	}

	public static Object findAttribute(final Object object, final String attribute) {
		if ((object == null) || (attribute == null)) {
			if (FunctionCaller.DEBUG.isDebugEnabled()) {
				FunctionCaller.DEBUG.debug("Some of the parameters are null");
			}
			return null;
		}
		if (FunctionCaller.DEBUG.isDebugEnabled()) {
			FunctionCaller.DEBUG.debug("FindAttribute() - Object " + object + " from class " + object.getClass().getName()
					+ ", Attribute: " + attribute);
		}
		try {
			return new Utils().getPublicAttribute(object, attribute);
		} catch (Exception e) {
			if (FunctionCaller.DEBUG.isDebugEnabled()) {
				FunctionCaller.DEBUG.debug("Exception: " + e.getMessage(), e);
			}
		}
		return null;
	}

	public static Object tryCallUpward(final Object object, final String method, final Object parameter) throws Exception {

		Class cls = parameter.getClass();

		while (!cls.equals(Object.class)) {
			Method meth = null;
			try {
				meth = object.getClass().getDeclaredMethod(method, cls);
				return meth.invoke(object, parameter);
			} catch (Exception e) {
				if (FunctionCaller.DEBUG.isDebugEnabled()) {
					FunctionCaller.DEBUG.debug("Exception: " + e.getMessage()
							+ " now going to try with interface type for parameter ");
				}

				for (Class interfaceInst : cls.getInterfaces()) {
					try {
						meth = object.getClass().getDeclaredMethod(method, interfaceInst);
						return meth.invoke(object, parameter);
					} catch (Exception ee) {
						if (FunctionCaller.DEBUG.isDebugEnabled()) {
							FunctionCaller.DEBUG.debug("Exception: " + ee.getMessage() + " now going to try with next interfaces");
						}
					}

				}
			}
			if (FunctionCaller.DEBUG.isDebugEnabled()) {
				FunctionCaller.DEBUG.debug("Now going to try with super class ");
			}
			cls = cls.getSuperclass();
		}
		return null;
	}

	public static String concat(final Object str1, final Object str2) {
		try {
			return str1.toString() + str2.toString();
		} catch (Exception e) {
			if (FunctionCaller.DEBUG.isDebugEnabled()) {
				FunctionCaller.DEBUG.debug("Exception: " + e + " for " + str1 + "," + str2);
			}
		}
		return null;
	}

	public static String[] split(final String string, final String pattern) {
		String[] array = FunctionCaller.arrayMap.get(string + "_" + pattern);
		if (array != null) {
			return array;
		}

		Pattern patternObj = FunctionCaller.patternMap.get(pattern);
		if (patternObj == null) {
			patternObj = Pattern.compile(pattern);
			FunctionCaller.patternMap.put(pattern, patternObj);
		}
		array = patternObj.split(string);
		FunctionCaller.arrayMap.put(string + "_" + pattern, array);

		return array;
	}

	public static List<?> asList(final Set<?> set) {
		try {
			List<AbstractEntity> list = new ArrayList<AbstractEntity>();
			for (AbstractEntity o : (Set<AbstractEntity>) set) {
				list.add(o);
			}
			Collections.sort(list);
			return list;
		} catch (Exception e) {
			if (FunctionCaller.DEBUG.isDebugEnabled()) {
				FunctionCaller.DEBUG.debug("Exception: " + e);
			}
		}
		return null;
	}

	public static String format(String formatter, Object value, String pattern) {
		return FunctionCaller.formatterFactory.format(formatter, value, pattern);
	}
}
